﻿using System;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;

namespace Mars.GpuInstance
{
    public class GPUSkinRenderBatch : GPURenderBatch
    {
        private static Vector4[] animationDatas = new Vector4[InstancerConstants.COUNT_PER_DRAW];
        private static Vector4[] clipDatas = new Vector4[InstancerConstants.COUNT_PER_DRAW];
        
        private NativeArray<float4> cullingAnimationData;  //裁剪后的动画数据
        private NativeArray<float4> cullingAnimatorControllerData; //裁剪后的动画控制器
        
        private NativeArray<float4x4> animationBakeData;
        private ComputeBuffer animationBakeBuffer;

        private bool waitingSubmitAnimateBuffer = false;

        public GPUSkinRenderBatch()
        {
            outputCount = new NativeArray<int>(1, Allocator.Persistent);
            outputIndex = new NativeList<int>(InstancerConstants.ADD_COUNT, Allocator.Persistent);
            boundVerts = new NativeArray<float4>(8, Allocator.Persistent);
        }
        
        public GPUSkinRenderBatch(Mesh _mesh, Material _mat, MaterialPropertyBlock _mpb, float2 _drawRange)
        {

            outputCount = new NativeArray<int>(1, Allocator.Persistent);
            outputIndex = new NativeList<int>(InstancerConstants.ADD_COUNT, Allocator.Persistent);
            boundVerts = new NativeArray<float4>(8, Allocator.Persistent);
            Init(_mesh, _mat, _mpb, _drawRange, float3.zero);
        }

        public override void CopyAnimationData( in NativeList<float4> animatorControllerData, in NativeList<float4> animationData, NativeList<JobHandle> handles)
        {
            var animationDataLen = outputIndex.Length * 2;
            if (!cullingAnimationData.IsCreated)
            {
                cullingAnimationData = new NativeArray<float4>(animationDataLen, Allocator.Persistent);
            }
            else if(cullingAnimationData.IsCreated)
            {
                if (animationDataLen > cullingAnimationData.Length)
                {
                    cullingAnimationData.Dispose();
                    cullingAnimationData = new NativeArray<float4>(animationDataLen, Allocator.Persistent);
                }
            }

            var controllerDataLen = outputIndex.Length * 2;
            if (!cullingAnimatorControllerData.IsCreated)
            {
                cullingAnimatorControllerData = new NativeArray<float4>(controllerDataLen, Allocator.Persistent);
            }
            else if(cullingAnimatorControllerData.IsCreated)
            {
                if (controllerDataLen > cullingAnimatorControllerData.Length)
                {
                    cullingAnimatorControllerData.Dispose();
                    cullingAnimatorControllerData = new NativeArray<float4>(outputIndex.Length * 2, Allocator.Persistent);
                }
            }
            
            var copyDataJob = new GPUAnimationCopyData();
            copyDataJob.outputIndex = outputIndex;
            copyDataJob.cullingAnimationData = cullingAnimationData;
            copyDataJob.cullingAnimatorControllerData = cullingAnimatorControllerData;
            copyDataJob.animationData = animationData;
            copyDataJob.animatorControllerData = animatorControllerData;
            var handle = copyDataJob.Schedule();
            handles.Add(handle);
        }
        
        public override JobHandle CalculateAnim(int frameRate, int textureSizeX, int totalBoneCount)
        {
            var instanceCount = GetInstanceCount();
            waitingSubmitAnimateBuffer = true;

            var animatorControllerJob = new GPUAnimatorControllerJob();
            animatorControllerJob.currentTime = Time.time;
            animatorControllerJob.frameRate = frameRate;
            animatorControllerJob.gpuiAnimationData = cullingAnimationData;
            animatorControllerJob.gpuiCrowdAnimatorController = cullingAnimatorControllerData;
            var controllerJobHandle = animatorControllerJob.Schedule(instanceCount, instanceCount);
            return controllerJobHandle;
        }
        

        public override void UpdateAnimationBakeBuffer()
        {
            mpb.SetBuffer(InstancerConstants.ANIMATION_BUFFER, animationBakeBuffer);
        }
        
        public override void SubmitToGPU()
        {
            base.SubmitToGPU();
            if (waitingSubmitAnimateBuffer)
            {
                SubmitAnimateBuffer();
            }
        }
        
        private void SubmitAnimateBuffer()
        {
            waitingSubmitAnimateBuffer = false;
            // if (animationBakeData.Length > 0)
            // {
            //     if (animationBakeBuffer != null && animationBakeBuffer.count >= animationBakeData.Length)
            //     {
            //         animationBakeBuffer.SetData(animationBakeData, 0, 0, animationBakeData.Length);
            //     }
            //     else
            //     {
            //         if (animationBakeBuffer != null)
            //         {
            //             animationBakeBuffer.Release();
            //         }
            //         animationBakeBuffer = new ComputeBuffer(animationBakeData.Length, sizeof(float) * 16);
            //         animationBakeBuffer.SetData(animationBakeData);
            //     }
            //
            //     UpdateAnimationBakeBuffer();
            // }
        }
        

        public override void Dispose()
        {
            base.Dispose();
            if (renderMesh)
            {
                renderMesh = null;
            }

            renderMat = null;

            if (cullingAnimationData.IsCreated)
            {
                cullingAnimationData.Dispose();
            }

            if (cullingAnimatorControllerData.IsCreated)
            {
                cullingAnimatorControllerData.Dispose();
            }

            if (animationBakeData.IsCreated)
            {
                animationBakeData.Dispose();
            }

            if (animationBakeBuffer != null)
            {
                animationBakeBuffer.Dispose();
                animationBakeBuffer = null;
            }
        }

        public override void OnRecycle()
        {
            base.OnRecycle();

            if (animationBakeBuffer != null)
            {
                animationBakeBuffer.Dispose();
                animationBakeBuffer = null;
            }
        }

        #region Draw

        public override void Draw(Bounds drawBounds, ShadowLod animatorShadowLod)
        {
            ShadowCastingMode shadowCastingMode = ShadowCastingMode.Off;
            if (animatorShadowLod == ShadowLod.FOLLOW_LOD)
            {
                shadowCastingMode = ShadowCastingMode.On;
            }
            DrawMeshInstanced(shadowCastingMode);
        }

        public override void DrawShadows(Bounds drawBounds)
        {
            DrawMeshInstanced(ShadowCastingMode.ShadowsOnly);
        }
        
        
        public override void DrawDebug(float sightDistance)
        {
        }
        
        #endregion

        #region 用DrawMeshInstanced绘制

        private void DrawMeshInstanced(ShadowCastingMode shadowCastingMode)
        {
            var instanceCount = GetInstanceCount();
            if (instanceCount <= 0)
            {
                return;
            }
            
            if (renderMesh == null)  //没有网格
            {
                return;
            }
            
            if (hasPoseOffset)
            {
                mpb.SetMatrix(InstancerConstants.BINDPOSE_OFFSET, bindPoseOffset);
            }
            
            int batchCount = (int)math.ceil((float)instanceCount / (float)InstancerConstants.COUNT_PER_DRAW);
   
            for (int i = 0; i < batchCount; i++)
            {
                var srcIndex = i * InstancerConstants.COUNT_PER_DRAW;
                var copyCount = InstancerConstants.COUNT_PER_DRAW;
                if (i == (batchCount - 1))
                {
                    copyCount = instanceCount - i * InstancerConstants.COUNT_PER_DRAW;
                }

                MathUtil.Copy<float4, Vector4>(campData, srcIndex, campColorDatas, 0, copyCount);
                MathUtil.Copy<float4x4, Matrix4x4>(output, srcIndex, matrices, 0, copyCount);

                for (int j = 0; j < copyCount; j++)
                {
                    animationDatas[j] = (cullingAnimationData[2 * j]);
                    clipDatas[j] = ( cullingAnimationData[2 * j + 1]);
                }
                
                mpb.SetVectorArray(InstancerConstants.GPU_AnimationData, animationDatas);
                mpb.SetVectorArray(InstancerConstants.GPU_CurrentClipData, clipDatas);
                mpb.SetVectorArray(InstancerConstants.GPU_CampColorData, campColorDatas);
                mpb.SetInteger(InstancerConstants.GPU_ANIM_LOD, lodLevel);

                DrawMeshInstanced(matrices,  copyCount, shadowCastingMode);
            }
        }
        #endregion
    }
}
